<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>

<script>
    /**
     * 实现DFS和BFS
     */
    // DFS
    const depthArr = [];
    function DFS(obj) {
        for (let i in obj) {
            depthArr.push(i);
            if (Object.prototype.toString.call(obj[i]) === '[object Object]') {
                DFS(obj[i]);
            }
        }
        return depthArr;
    }
    // BFS
    const bfsArr = [];
    function BFS(obj) {
        // 获取所有键
        const arr = Object.keys(obj);
        bfsArr = bfsArr.concat(arr);
        arr.forEach(i => {
            if (Object.prototype.toString.call(obj[i]) === '[object Object]') {
                BFS(obj[i]);
            }
        });

        return bfsArr;
    }


    /**
     * 实现浅拷贝与深拷贝
     */
    // 浅拷贝
    function shallowClone(obj) {
        const newObj = {};
        for (let prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                newObj[prop] = obj[prop];
            }
        }
        return newObj;
    }

    // function shallowClone(obj) {
    //     const newObj = {};
    //     for (let prop in obj) {
    //         if (obj.hasOwnProperty(prop)) {
    //             newObj[prop] = obj[prop];
    //         }
    //     }
    // }

    // 深拷贝（使用递归拷贝所有层级属性）
    function deepClone(obj) {
        if (typeof obj === 'number' || typeof obj === 'string' || typeof obj === 'boolean' || typeof obj === 'undefined') {
            return obj;
        } else if (obj === null) {
            return obj;
        } else {
            let newObj = obj instanceof Array ? [] : {};
            for (let prop in obj) {
                if (obj.hasOwnProperty(prop)) {
                    newObj[prop] = deepClone(obj[prop]);
                }
            }
            return newObj;
        }
    }

    function deepClone(obj) {
        if (typeof obj === 'number' || typeof obj === 'string' || typeof obj === 'boolean' || typeof obj === 'undefined') {
            return obj;
        } else if (obj === null) {
            return obj;
        } else {
            let newObj = Array.isArray(obj) ? [] : {};
            for (let prop in obj) {
                if (obj.hasOwnProperty(prop)) {
                    newObj[prop] = deepClone(obj[prop]);
                }
            }
            return newObj;
        }
    }

    // 用深度优先思想实现一个深拷贝函数
    /**
     * 深度拷贝最主要需要考虑的因素就是需要考虑js的各种数据类型
     * 1）6种基本类型(number,string,null,undefined,boolean,symbol)不需要处理，直接复制返回即可
     * 2）几种特殊类型 date、regexp、set、map，直接new一遍即可
     * 3）需要着重处理的类型 Array、Object，直接使用递归处理即可
     * 4）递归处理Array、Object的过程中需要考虑循环引用，循环引用处理可以利用WeakMap将每次递归的对象存储起来，如果一样，直接返回
     */
    function deepClone(obj, hash = new WeakMap()) {
        if (obj === null) return obj;
        if (obj instanceof Date) return new Date(obj);
        if (obj instanceof RegExp) return new RegExp(obj);
        if (typeof obj !== "object")
            return obj;
        if (hash.get(obj))  return hash.get(obj);
        let cloneObj = new obj.constructor();
        hash.set(obj, cloneObj);
        for (let prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                cloneObj[prop] = deepClone(obj[prop], hash);
            }
        }
        return cloneObj;
    }

    /**
     * 手写new
    */
    function myNew(Func, ...args) {
        // 创建一个新的对象
        const obj = {};
        // 新对象原型指向构造函数原型对象
        obj.__proto__ = Func.prototype;
        // 将构造函数的this指向新对象
        let result = Func.apply(obj, args);
        // 根据返回值判断
        return result instanceof Object ? result : obj;

    }
    // 测试myNew
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype.say = function() {
        console.log(this.name);
    }
    let p = myNew(Person, "zc", "hhh");
    console.log(p);
    p.say();

    /**
     * 实现instanceof
    */ 
    function newInstanceOf(leftValue, rightValue) {
        let rightProto = rightValue.prototype;
        leftValue = leftValue.__proto__;
        while (true) {
            if (leftValue === null) {
                return false;
            }
            if (leftValue === rightProto) {
                return true;
            }
            leftValue = leftValue.__proto__;
        }
    }

    /**
     * 防抖和节流 
    */
    // 节流
    function throttled(fn, delay) {
        let timer = null;
        let startTime = Date.now();
        return function() {
            let curTime = Date.now(); // 当前时间
            let remaining = delay - (curTime - startTime); // 从上一次到现在还剩余的时间
            let context = this; // 保存当前上下文
            let args = arguments;
            clearTimeout(timer);
            if (remaining <= 0) { // 到时间了，该发送了
                fn.apply(context, args);
                startTime = Date.now();
            } else {
                timer = setTimeout(fn, remaining);
            }
        }
    }

    function throttled(fn, delay) {
        let startTime = Date.now();
        let timer = null;
        return function() {
            let curTime = Date.now();
            let remaining = delay - (curTime - startTime);
            let context = this;
            let args = arguments;
            if (remaining <= 0) {
                fn.apply(context, args);
                startTime = Date.now();
            } else {
                timer = setTimeout(fn, remaining);
            }
        }
    }

    // 防抖(easy)
    function debounce(func, wait) {
        let timeout;
        return function() {
            let context = this; // 保存当前上下文
            let args = arguments;

            clearTimeout(timeout);
            timeout = setTimeout(function() {
                func.apply(context, args);
            }, wait);

        }
    }

    function debounce(fn, wait) {
        let timer;
        return function() {
            let context = this;
            let args = arguments;
            cleartTimeout(timer);
            timer = setTimeout(function() {
                fn.apply(context, args);
            }, wait);
        }
    }
    // 防抖(plus) 防抖立即执行
    function debouncePlus(func, wait, immediate) {
        let timeout;
        return function () {
            let context = this;
            let args = arguments;

            if (timeout) // timeout不为null
                clearTimeout(timeout);
            if (immediate) {
                let callNow = !timeout;
                timeout = setTimeout(function() {
                    timeout = null;
                }, wait);
                if (callNow) {
                    func.apply(context, args);
                }
            } else {
                timeout = setTimeout(function() {
                    func.apply(context, args);
                }, wait);
            }
        }
    }

    console.log("============================");

    /**
     * 观察者模式
     * 
    */
    // 被观察者
    class Subject {
        // 构造函数
        constructor() {
            this.observerList = []; // 观察者列表
        }

        // 添加观察者
        addObserver(observer) {
            this.observerList.push(observer);
        }

        // 移出观察者
        removeObserver(observer) {
            const index = this.observerList.findIndex(obj => obj.name === observer.name);
            this.observerList.splice(index, 1);
        }

        // 通知观察者
        notifyObservers(message) {
            const observers = this.observerList;
            observers.forEach(observer => observer.notified(message));
        }
    }

    // 观察者
    class Observer {
        constructor(name, subject) {
            this.name = name;
            if (subject) {
                subject.addObserver(this); // 在被观察者中添加观察者
            }
        }

        notified(message) { // 被通知
            console.log(this.name, 'got message', message);
        }
    }

    // 使用
    const subject = new Subject();
    const observerA = new Observer('observerA', subject);
    const observerB = new Observer('observerB');
    subject.addObserver(observerB);
    subject.notifyObservers('Hello from subject');
    subject.removeObserver(observerA);
    subject.notifyObservers('Hello again');


    console.log("============================");


    /**
     * 发布订阅模式
    */
    class PubSub {
        constructor() {
            this.messages = {};
            this.listeners = {};
        }
        // 添加发布者
        publish(type, content) {
            const existContent = this.messages[type];
            if (!existContent) {
                this.messages[type] = [];
            }
            this.messages[type].push(content);
        }
        // 添加订阅者
        subscribe(type, cb) {
            const existListener = this.listeners[type];
            if (!existListener) {
                this.listeners[type] = [];
            }
            this.listeners[type].push(cb);
        }
        // 通知
        notify(type) {
            const messages = this.messages[type];
            const subscribers = this.listeners[type] || [];
            subscribers.forEach((cb, index) => cb(messages[index]));
        }
    }

    // 发布者
    class Publisher {
        constructor(name, context) {
            this.name = name;
            this.context = context;
        }
        publish(type, content) {
            this.context.publish(type, content);
        }
    }

    // 订阅者
    class Subscriber {
        constructor(name, context) {
            this.name = name;
            this.context = context;
        }
        subscribe(type, cb) {
            this.context.subscribe(type, cb);
        }
    }

    // 使用
    const TYPE_A = "music";
    const TYPE_B = "movie";
    const TYPE_C = "novel";

    const pubsub = new PubSub();

    const publisherA = new Publisher('publisherA', pubsub);
    const publisherB = new Publisher('publisherA', pubsub);
    const publisherC = new Publisher('publisherA', pubsub);

    publisherA.publish(TYPE_A, 'we are young');
    publisherA.publish(TYPE_B, 'the silicon valley');
    publisherB.publish(TYPE_A, 'stronger');
    publisherC.publish(TYPE_C, 'a brief history of time');

    const subscriberA = new Subscriber('subscriberA', pubsub);
    subscriberA.subscribe(TYPE_A, res => {
        console.log('subscriberA received', res);
    });
    const subscriberB = new Subscriber('subscriberB', pubsub);
    subscriberB.subscribe(TYPE_C, res => {
        console.log('subscriberB received', res)
    });
    const subscriberC = new Subscriber('subscriberC', pubsub);
    subscriberC.subscribe(TYPE_B, res => {
        console.log('subscriberC received', res)
    });

    pubsub.notify(TYPE_A);
    pubsub.notify(TYPE_B);
    pubsub.notify(TYPE_C);


    console.log("============================");

    /**
     * 工厂模式
    */
    //工厂方法
    function Factory(career) {
        if (this instanceof Factory) {
            var a = new this[career]();
            return a;
        } else {
            return new Factory(career);
        }
    }
    // 工厂方法函数的原型中设置所有对象的构造函数
    Factory.prototype = {
        'coder': function () {
            this.careerName = '程序员'
            this.work = ['写代码', '修Bug']
        },
        'hr': function () {
            this.careerName = 'HR'
            this.work = ['招聘', '员工信息管理']
        },
        'driver': function () {
            this.careerName = '司机'
            this.work = ['开车']
        },
        'boss': function () {
            this.careerName = '老板'
            this.work = ['喝茶', '开会', '审批文件']
        }
    }
    let coder = new Factory('coder')
    console.log(coder)
    let hr = new Factory('hr')
    console.log(hr)

    console.log("============================");

    /**
     * 柯里化函数
    */
    // 原始函数
    function Fn_init (a, b, c) {
        console.log(a, b, c, a * b * c);
    }

    function curryization(fn, params) {
        const lth = fn.length;

        params = params || [];
        console.log('params', params);

        return function (...args) {
            // 收集fn函数的参数
            newArgs = params.concat(args);
            console.log('newArgs', newArgs);

            if (newArgs.length < lth) {
                // 继续执行curryization柯里化函数，继续收集参数，this指向window
                return curryization.call(this, fn, newArgs);
            } else {
                // 所有参数收集完毕，整体执行源函数，this指向window
                return fn.apply(this, newArgs);
            }
        }
    }
    const curryFunc = curryization(Fn_init);

    curryFunc(2)(3, 4);

    /**
     * 使用Proxy实现链式调用
    */
    const Chainable = function (obj) {
        return new Proxy(obj, {
            get(target, prop) {
                console.log('target', target);
                if (prop in target) {
                    if (typeof target[prop] === 'function') {
                        return (...args) => {
                            target[prop](...args);
                            return Chainable(target);
                        };
                    }
                    return Chainable(target[prop]);
                }
                throw new Error(`Property ${prop} not found`);
            }
        });
    };

    const api = {
        add(x) {
            this.value += x;
        },
        subtract(x) {
            this.value -= x;
        },
        getValue() {
            console.log(this.value);
        }
    };

    const chain = Chainable(api);

    chain.add(5).subtract(3).getValue(); // Output: 2


    console.log("============================");

    /**
     * 原型链继承  引用类型的属性被所有实例共享
    */
    function Parent() {
        this.name = 'zc';
    }
    Parent.prototype.getName = function() {
        console.log(this.name);
    }
    function Child() {}
    // 继承
    Child.prototype = new Parent();
    var child1 = new Child();
    child1.getName();

    // 寄生组合式继承
    function create(parent, child) {
        child.prototype = Object.create(parent.prototype);
        child.prototype.constructor = child;
    }
    function Parent() {
        this.name = 'parent';
    }
    Parent.prototype.getName = function() {
        return this.name;
    }
    function Child() {
        Parent.call(this);
        this.friends = 'child';
    }

    /**
     * 虚拟节点转真实DOM结点
    */
   const vnode = {
        tag: 'DIV',
        attrs: {
            id: 'app'
        },
        children: [{
            tag: 'SPAN',
            children: [{
                tag: 'A',
                children: []
            }]
        },
        {
            tag: 'SPAN',
            children: [{
                tag: 'A',
                children: []
            },
            {
                tag: 'A',
                children: []
            }
            ]
        }
        ]
    }


    /*虚拟dom转换真实dom设计*/
    function render(vnode) {
        //核心递归函数
        const main = (node, parent = null) => {
            let ele
            // 处理非对象
            if (ele instanceof Object)
                ele = document.createElement(node.tag.toLowerCase())
            else
                ele = document.createTextNode(node);
            //添加属性
            if (node.attrs) {
                for (let key in node.attrs) {
                    ele.setAttribute(key, node.attrs[key])
                }
            }
            //添加孩子，递归
            if (node.children && node.children.length) {
                for (let val of node.children) {
                    main(val, ele)
                }
            }
            if (parent) {
                parent.appendChild(ele)
            } else {
                parent = ele
            }
            return parent
        }
        let dom = main(vnode)
        return dom
    }

    render(vnode)


</script>

</html>